// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
#include "myboxwidget.h"

#include "vtkActor.h"
#include "vtkAssemblyNode.h"
#include "vtkAssemblyPath.h"
#include "vtkCallbackCommand.h"
#include "vtkCamera.h"
#include "vtkCellArray.h"
#include "vtkCellPicker.h"
#include "vtkDoubleArray.h"
#include "vtkFloatArray.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkPickingManager.h"
#include "vtkPlanes.h"
#include "vtkPolyData.h"
#include "vtkPolyDataMapper.h"
#include "vtkProperty.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkSphereSource.h"
#include "vtkTransform.h"
#include "vtkConeSource.h"

VTK_ABI_NAMESPACE_BEGIN
    vtkStandardNewMacro(MyBoxWidget);

MyBoxWidget::MyBoxWidget()
{
    this->State = MyBoxWidget::Start;
    this->EventCallbackCommand->SetCallback(MyBoxWidget::ProcessEvents);

    // Enable/disable the translation, rotation, and scaling of the widget
    this->TranslationEnabled = 1;
    this->RotationEnabled = 1;
    this->ScalingEnabled = 1;

    // Build the representation of the widget
    int i;

    // Control orientation of normals
    this->InsideOut = 0;
    this->OutlineFaceWires = 0;
    this->OutlineCursorWires = 1;

    // Construct the poly data representing the hex
    this->HexPolyData = vtkPolyData::New();
    this->HexMapper = vtkPolyDataMapper::New();
    this->HexMapper->SetInputData(HexPolyData);
    this->HexActor = vtkActor::New();
    this->HexActor->SetMapper(this->HexMapper);

    // Construct initial points
    this->Points = vtkPoints::New(VTK_DOUBLE);
    this->Points->SetNumberOfPoints(15); // 8 corners; 6 faces; 1 center
    this->HexPolyData->SetPoints(this->Points);

    // Construct connectivity for the faces. These are used to perform
    // the picking.
    vtkIdType pts[4];
    vtkCellArray* cells = vtkCellArray::New();
    cells->AllocateEstimate(6, 4);
    pts[0] = 3;
    pts[1] = 0;
    pts[2] = 4;
    pts[3] = 7;
    cells->InsertNextCell(4, pts);
    pts[0] = 1;
    pts[1] = 2;
    pts[2] = 6;
    pts[3] = 5;
    cells->InsertNextCell(4, pts);
    pts[0] = 0;
    pts[1] = 1;
    pts[2] = 5;
    pts[3] = 4;
    cells->InsertNextCell(4, pts);
    pts[0] = 2;
    pts[1] = 3;
    pts[2] = 7;
    pts[3] = 6;
    cells->InsertNextCell(4, pts);
    pts[0] = 0;
    pts[1] = 3;
    pts[2] = 2;
    pts[3] = 1;
    cells->InsertNextCell(4, pts);
    pts[0] = 4;
    pts[1] = 5;
    pts[2] = 6;
    pts[3] = 7;
    cells->InsertNextCell(4, pts);
    this->HexPolyData->SetPolys(cells);
    cells->Delete();
    this->HexPolyData->BuildCells();

    // The face of the hexahedra
    cells = vtkCellArray::New();
    cells->AllocateEstimate(1, 4);
    cells->InsertNextCell(4, pts); // temporary, replaced later
    this->HexFacePolyData = vtkPolyData::New();
    this->HexFacePolyData->SetPoints(this->Points);
    this->HexFacePolyData->SetPolys(cells);
    this->HexFaceMapper = vtkPolyDataMapper::New();
    this->HexFaceMapper->SetInputData(HexFacePolyData);
    this->HexFace = vtkActor::New();
    this->HexFace->SetMapper(this->HexFaceMapper);
    cells->Delete();

    // Create the outline for the hex(十字架和轮廓)
    this->OutlinePolyData = vtkPolyData::New();
    this->OutlinePolyData->SetPoints(this->Points);
    this->OutlineMapper = vtkPolyDataMapper::New();
    this->OutlineMapper->SetInputData(this->OutlinePolyData);
    this->HexOutline = vtkActor::New();
    this->HexOutline->SetMapper(this->OutlineMapper);
    cells = vtkCellArray::New();
    cells->AllocateEstimate(15, 2);
    this->OutlinePolyData->SetLines(cells);
    cells->Delete();

    // Set up the initial properties
    this->CreateDefaultProperties();

    // Create the outline
    this->GenerateOutline();

    // Create the handles
    this->Handle = new vtkActor*[3];
    this->HandleMapper = new vtkPolyDataMapper*[3];
    this->HandleGeometry = new vtkConeSource*[3];
    for (i = 0; i < 3; i++)
    {
        this->HandleGeometry[i] = vtkConeSource::New();

        this->HandleGeometry[i]->SetHeight(3.0);       // 设置高度
        this->HandleGeometry[i]->SetRadius(1.0);       // 设置底部半径
        this->HandleGeometry[i]->SetResolution(50);    // 设置分辨率（多边形边数）
        this->HandleGeometry[i]->SetDirection(1, 0, 0); // 设置方向（沿 X 轴）

        this->HandleMapper[i] = vtkPolyDataMapper::New();
        this->HandleMapper[i]->SetInputConnection(this->HandleGeometry[i]->GetOutputPort());
        this->Handle[i] = vtkActor::New();
        this->Handle[i]->SetMapper(this->HandleMapper[i]);
    }

    // Define the point coordinates
    double bounds[6];
    bounds[0] = -0.5;
    bounds[1] = 0.5;
    bounds[2] = -0.5;
    bounds[3] = 0.5;
    bounds[4] = -0.5;
    bounds[5] = 0.5;
    // Points 8-14 are down by PositionHandles();
    this->PlaceWidget(bounds);

    // Manage the picking stuff
    this->HandlePicker = vtkCellPicker::New();
    this->HandlePicker->SetTolerance(0.001);
    for (i = 0; i < 3; i++)
    {
        this->HandlePicker->AddPickList(this->Handle[i]);
    }
    this->HandlePicker->PickFromListOn();

    this->HexPicker = vtkCellPicker::New();
    this->HexPicker->SetTolerance(0.001);
    this->HexPicker->AddPickList(HexActor);
    this->HexPicker->PickFromListOn();

    this->CurrentHandle = nullptr;

    this->Transform = vtkTransform::New();
}

MyBoxWidget::~MyBoxWidget()
{
    this->HexActor->Delete();
    this->HexMapper->Delete();
    this->HexPolyData->Delete();
    this->Points->Delete();

    this->HexFace->Delete();
    this->HexFaceMapper->Delete();
    this->HexFacePolyData->Delete();

    this->HexOutline->Delete();
    this->OutlineMapper->Delete();
    this->OutlinePolyData->Delete();

    for (int i = 0; i < 3; i++)
    {
        this->HandleGeometry[i]->Delete();
        this->HandleMapper[i]->Delete();
        this->Handle[i]->Delete();
    }
    delete[] this->Handle;
    delete[] this->HandleMapper;
    delete[] this->HandleGeometry;

    this->HandlePicker->Delete();
    this->HexPicker->Delete();

    this->Transform->Delete();

    this->HandleProperty->Delete();
    this->SelectedHandleProperty->Delete();
    this->FaceProperty->Delete();
    this->SelectedFaceProperty->Delete();
    this->OutlineProperty->Delete();
    this->SelectedOutlineProperty->Delete();
}

void MyBoxWidget::SetEnabled(int enabling)
{
    if (!this->Interactor)
    {
        vtkErrorMacro(<< "The interactor must be set prior to enabling/disabling widget");
        return;
    }

    if (enabling) //------------------------------------------------------------
    {
        vtkDebugMacro(<< "Enabling widget");

        if (this->Enabled) // already enabled, just return
        {
            return;
        }

        if (!this->CurrentRenderer)
        {
            this->SetCurrentRenderer(this->Interactor->FindPokedRenderer(
                this->Interactor->GetLastEventPosition()[0], this->Interactor->GetLastEventPosition()[1]));
            if (this->CurrentRenderer == nullptr)
            {
                return;
            }
        }

        this->Enabled = 1;

        // listen to the following events
        vtkRenderWindowInteractor* i = this->Interactor;
        i->AddObserver(vtkCommand::MouseMoveEvent, this->EventCallbackCommand, this->Priority);
        i->AddObserver(vtkCommand::LeftButtonPressEvent, this->EventCallbackCommand, this->Priority);
        i->AddObserver(vtkCommand::LeftButtonReleaseEvent, this->EventCallbackCommand, this->Priority);
        i->AddObserver(vtkCommand::MiddleButtonPressEvent, this->EventCallbackCommand, this->Priority);
        i->AddObserver(
            vtkCommand::MiddleButtonReleaseEvent, this->EventCallbackCommand, this->Priority);
        i->AddObserver(vtkCommand::RightButtonPressEvent, this->EventCallbackCommand, this->Priority);
        i->AddObserver(vtkCommand::RightButtonReleaseEvent, this->EventCallbackCommand, this->Priority);

        // Add the various actors
        // Add the outline
        this->CurrentRenderer->AddActor(this->HexActor);
        this->CurrentRenderer->AddActor(this->HexOutline);
        this->HexActor->SetProperty(this->OutlineProperty);
        this->HexOutline->SetProperty(this->OutlineProperty);

        // Add the hex face
        this->CurrentRenderer->AddActor(this->HexFace);
        this->HexFace->SetProperty(this->FaceProperty);

        // turn on the handles
        for (int j = 0; j < 3; j++)
        {
            this->CurrentRenderer->AddActor(this->Handle[j]);
            this->Handle[j]->SetProperty(this->HandleProperty);
        }
        this->RegisterPickers();

        this->InvokeEvent(vtkCommand::EnableEvent, nullptr);
    }

    else // disabling-------------------------------------------------------------
    {
        vtkDebugMacro(<< "Disabling widget");

        if (!this->Enabled) // already disabled, just return
        {
            return;
        }

        this->Enabled = 0;

        // don't listen for events any more
        this->Interactor->RemoveObserver(this->EventCallbackCommand);

        // turn off the outline
        this->CurrentRenderer->RemoveActor(this->HexActor);
        this->CurrentRenderer->RemoveActor(this->HexOutline);

        // turn off the hex face
        this->CurrentRenderer->RemoveActor(this->HexFace);

        // turn off the handles
        for (int i = 0; i < 3; i++)
        {
            this->CurrentRenderer->RemoveActor(this->Handle[i]);
        }

        this->CurrentHandle = nullptr;
        this->InvokeEvent(vtkCommand::DisableEvent, nullptr);
        this->SetCurrentRenderer(nullptr);
        this->UnRegisterPickers();
    }

    this->Interactor->Render();
}

void MyBoxWidget::ProcessEvents(
    vtkObject* vtkNotUsed(object), unsigned long event, void* clientdata, void* vtkNotUsed(calldata))
{
    MyBoxWidget* self = reinterpret_cast<MyBoxWidget*>(clientdata);

    // okay, let's do the right thing
    switch (event)
    {
    case vtkCommand::LeftButtonPressEvent:
        self->OnLeftButtonDown();
        break;
    case vtkCommand::LeftButtonReleaseEvent:
        self->OnLeftButtonUp();
        break;
    case vtkCommand::MiddleButtonPressEvent:
        self->OnMiddleButtonDown();
        break;
    case vtkCommand::MiddleButtonReleaseEvent:
        self->OnMiddleButtonUp();
        break;
    case vtkCommand::RightButtonPressEvent:
        self->OnRightButtonDown();
        break;
    case vtkCommand::RightButtonReleaseEvent:
        self->OnRightButtonUp();
        break;
    case vtkCommand::MouseMoveEvent:
        self->OnMouseMove();
        break;
    }
}

void MyBoxWidget::PrintSelf(ostream& os, vtkIndent indent)
{
    this->Superclass::PrintSelf(os, indent);

    double* bounds = this->InitialBounds;
    os << indent << "Initial Bounds: "
       << "(" << bounds[0] << "," << bounds[1] << ") "
       << "(" << bounds[2] << "," << bounds[3] << ") "
       << "(" << bounds[4] << "," << bounds[5] << ")\n";

    if (this->HandleProperty)
    {
        os << indent << "Handle Property: " << this->HandleProperty << "\n";
    }
    else
    {
        os << indent << "Handle Property: (none)\n";
    }
    if (this->SelectedHandleProperty)
    {
        os << indent << "Selected Handle Property: " << this->SelectedHandleProperty << "\n";
    }
    else
    {
        os << indent << "SelectedHandle Property: (none)\n";
    }

    if (this->FaceProperty)
    {
        os << indent << "Face Property: " << this->FaceProperty << "\n";
    }
    else
    {
        os << indent << "Face Property: (none)\n";
    }
    if (this->SelectedFaceProperty)
    {
        os << indent << "Selected Face Property: " << this->SelectedFaceProperty << "\n";
    }
    else
    {
        os << indent << "Selected Face Property: (none)\n";
    }

    if (this->OutlineProperty)
    {
        os << indent << "Outline Property: " << this->OutlineProperty << "\n";
    }
    else
    {
        os << indent << "Outline Property: (none)\n";
    }
    if (this->SelectedOutlineProperty)
    {
        os << indent << "Selected Outline Property: " << this->SelectedOutlineProperty << "\n";
    }
    else
    {
        os << indent << "Selected Outline Property: (none)\n";
    }

    os << indent << "Outline Face Wires: " << (this->OutlineFaceWires ? "On\n" : "Off\n");
    os << indent << "Outline Cursor Wires: " << (this->OutlineCursorWires ? "On\n" : "Off\n");
    os << indent << "Inside Out: " << (this->InsideOut ? "On\n" : "Off\n");
    os << indent << "Translation Enabled: " << (this->TranslationEnabled ? "On\n" : "Off\n");
    os << indent << "Scaling Enabled: " << (this->ScalingEnabled ? "On\n" : "Off\n");
    os << indent << "Rotation Enabled: " << (this->RotationEnabled ? "On\n" : "Off\n");
}

#define VTK_AVERAGE(a, b, c)                                                                       \
do                                                                                               \
    {                                                                                                \
            c[0] = (a[0] + b[0]) / 2.0;                                                                    \
            c[1] = (a[1] + b[1]) / 2.0;                                                                    \
            c[2] = (a[2] + b[2]) / 2.0;                                                                    \
    } while (false)

    void MyBoxWidget::PositionHandles()
{
    double* pts = static_cast<vtkDoubleArray*>(this->Points->GetData())->GetPointer(0);
    double* p0 = pts;
    double* p1 = pts + 3 * 1;
    double* p2 = pts + 3 * 2;
    double* p3 = pts + 3 * 3;
    // double *p4 = pts + 3*4;
    double* p5 = pts + 3 * 5;
    double* p6 = pts + 3 * 6;
    double* p7 = pts + 3 * 7;
    double x[3];

    VTK_AVERAGE(p0, p7, x);
    this->Points->SetPoint(8, x);
    VTK_AVERAGE(p1, p6, x);
    this->Points->SetPoint(9, x);
    VTK_AVERAGE(p0, p5, x);
    this->Points->SetPoint(10, x);
    VTK_AVERAGE(p2, p7, x);
    this->Points->SetPoint(11, x);
    VTK_AVERAGE(p1, p3, x);
    this->Points->SetPoint(12, x);
    VTK_AVERAGE(p5, p7, x);
    this->Points->SetPoint(13, x);
    VTK_AVERAGE(p0, p6, x);
    this->Points->SetPoint(14, x);

    int i;
    for (i = 0; i < 3; ++i)
    {
        this->HandleGeometry[i]->SetCenter(this->Points->GetPoint(8 + i));
    }

    this->Points->GetData()->Modified();
    this->HexFacePolyData->Modified();
    this->HexPolyData->Modified();
    this->GenerateOutline();
}
#undef VTK_AVERAGE

void MyBoxWidget::HandlesOn()
{
    for (int i = 0; i < 3; i++)
    {
        this->Handle[i]->VisibilityOn();
    }
}

void MyBoxWidget::HandlesOff()
{
    for (int i = 0; i < 3; i++)
    {
        this->Handle[i]->VisibilityOff();
    }
}

void MyBoxWidget::SizeHandles()
{
    double radius = this->vtk3DWidget::SizeHandles(1.5);
    for (int i = 0; i < 3; i++)
    {
        this->HandleGeometry[i]->SetRadius(radius);
    }
}

int MyBoxWidget::HighlightHandle(vtkProp* prop)
{
    // first unhighlight anything picked
    this->HighlightOutline(0);
    if (this->CurrentHandle)
    {
        this->CurrentHandle->SetProperty(this->HandleProperty);
    }

    this->CurrentHandle = static_cast<vtkActor*>(prop);

    if (this->CurrentHandle)
    {
        this->CurrentHandle->SetProperty(this->SelectedHandleProperty);
        for (int i = 0; i < 6; i++) // find attached face
        {
            if (this->CurrentHandle == this->Handle[i])
            {
                return i;
            }
        }
    }

    if (this->CurrentHandle == this->Handle[6])
    {
        this->HighlightOutline(1);
    }

    return -1;
}

void MyBoxWidget::HighlightFace(int cellId)
{
    if (cellId >= 0)
    {
        vtkIdType npts;
        const vtkIdType* pts;
        vtkCellArray* cells = this->HexFacePolyData->GetPolys();
        this->HexPolyData->GetCellPoints(cellId, npts, pts);
        cells->ReplaceCellAtId(0, npts, pts);
        cells->Modified();
        this->CurrentHexFace = cellId;
        this->HexFace->SetProperty(this->SelectedFaceProperty);
        if (!this->CurrentHandle)
        {
            this->CurrentHandle = this->HexFace;
        }
    }
    else
    {
        this->HexFace->SetProperty(this->FaceProperty);
        this->CurrentHexFace = -1;
    }
}

void MyBoxWidget::HighlightOutline(int highlight)
{
    if (highlight)
    {
        this->HexActor->SetProperty(this->SelectedOutlineProperty);
        this->HexOutline->SetProperty(this->SelectedOutlineProperty);
    }
    else
    {
        this->HexActor->SetProperty(this->OutlineProperty);
        this->HexOutline->SetProperty(this->OutlineProperty);
    }
}

void MyBoxWidget::OnLeftButtonDown()
{
    int X = this->Interactor->GetEventPosition()[0];
    int Y = this->Interactor->GetEventPosition()[1];

    // Okay, we can process this. Try to pick handles first;
    // if no handles picked, then pick the bounding box.
    if (!this->CurrentRenderer || !this->CurrentRenderer->IsInViewport(X, Y))
    {
        this->State = MyBoxWidget::Outside;
        return;
    }

    vtkAssemblyPath* path = this->GetAssemblyPath(X, Y, 0., this->HandlePicker);

    if (path != nullptr)
    {
        this->State = MyBoxWidget::Moving;
        this->HighlightFace(this->HighlightHandle(path->GetFirstNode()->GetViewProp()));
        this->HandlePicker->GetPickPosition(this->LastPickPosition);
        this->ValidPick = 1;
    }
    else
    {
        path = this->GetAssemblyPath(X, Y, 0., this->HexPicker);

        if (path != nullptr)
        {
            this->State = MyBoxWidget::Moving;
            this->HexPicker->GetPickPosition(this->LastPickPosition);
            this->ValidPick = 1;
            if (!this->Interactor->GetShiftKey())
            {
                this->HighlightHandle(nullptr);
                this->HighlightFace(this->HexPicker->GetCellId());
            }
            else
            {
                this->CurrentHandle = this->Handle[6];
                this->HighlightOutline(1);
            }
        }
        else
        {
            this->HighlightFace(this->HighlightHandle(nullptr));
            this->State = MyBoxWidget::Outside;
            return;
        }
    }

    this->EventCallbackCommand->SetAbortFlag(1);
    this->StartInteraction();
    this->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr);
    this->Interactor->Render();
}

void MyBoxWidget::OnLeftButtonUp()
{
    if (this->State == MyBoxWidget::Outside || this->State == MyBoxWidget::Start)
    {
        return;
    }

    this->State = MyBoxWidget::Start;
    this->HighlightFace(this->HighlightHandle(nullptr));
    this->SizeHandles();

    this->EventCallbackCommand->SetAbortFlag(1);
    this->EndInteraction();
    this->InvokeEvent(vtkCommand::EndInteractionEvent, nullptr);
    this->Interactor->Render();
}

void MyBoxWidget::OnMiddleButtonDown()
{
    int X = this->Interactor->GetEventPosition()[0];
    int Y = this->Interactor->GetEventPosition()[1];

    // Okay, we can process this. Try to pick handles first;
    // if no handles picked, then pick the bounding box.
    if (!this->CurrentRenderer || !this->CurrentRenderer->IsInViewport(X, Y))
    {
        this->State = MyBoxWidget::Outside;
        return;
    }

    vtkAssemblyPath* path = this->GetAssemblyPath(X, Y, 0., this->HandlePicker);

    if (path != nullptr)
    {
        this->State = MyBoxWidget::Moving;
        this->CurrentHandle = this->Handle[6];
        this->HighlightOutline(1);
        this->HandlePicker->GetPickPosition(this->LastPickPosition);
        this->ValidPick = 1;
    }
    else
    {
        path = this->GetAssemblyPath(X, Y, 0., this->HexPicker);

        if (path != nullptr)
        {
            this->State = MyBoxWidget::Moving;
            this->CurrentHandle = this->Handle[6];
            this->HighlightOutline(1);
            this->HexPicker->GetPickPosition(this->LastPickPosition);
            this->ValidPick = 1;
        }
        else
        {
            this->HighlightFace(this->HighlightHandle(nullptr));
            this->State = MyBoxWidget::Outside;
            return;
        }
    }

    this->EventCallbackCommand->SetAbortFlag(1);
    this->StartInteraction();
    this->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr);
    this->Interactor->Render();
}

void MyBoxWidget::OnMiddleButtonUp()
{
    if (this->State == MyBoxWidget::Outside || this->State == MyBoxWidget::Start)
    {
        return;
    }

    this->State = MyBoxWidget::Start;
    this->HighlightFace(this->HighlightHandle(nullptr));
    this->SizeHandles();

    this->EventCallbackCommand->SetAbortFlag(1);
    this->EndInteraction();
    this->InvokeEvent(vtkCommand::EndInteractionEvent, nullptr);
    this->Interactor->Render();
}

void MyBoxWidget::OnRightButtonDown()
{
    int X = this->Interactor->GetEventPosition()[0];
    int Y = this->Interactor->GetEventPosition()[1];

    // Okay, we can process this. Try to pick handles first;
    // if no handles picked, then pick the bounding box.
    if (!this->CurrentRenderer || !this->CurrentRenderer->IsInViewport(X, Y))
    {
        this->State = MyBoxWidget::Outside;
        return;
    }

    vtkAssemblyPath* path = this->GetAssemblyPath(X, Y, 0., this->HandlePicker);

    if (path != nullptr)
    {
        this->State = MyBoxWidget::Scaling;
        this->HighlightOutline(1);
        this->HandlePicker->GetPickPosition(this->LastPickPosition);
        this->ValidPick = 1;
    }
    else
    {
        path = this->GetAssemblyPath(X, Y, 0., this->HexPicker);

        if (path != nullptr)
        {
            this->State = MyBoxWidget::Scaling;
            this->HighlightOutline(1);
            this->HexPicker->GetPickPosition(this->LastPickPosition);
            this->ValidPick = 1;
        }
        else
        {
            this->State = MyBoxWidget::Outside;
            return;
        }
    }

    this->EventCallbackCommand->SetAbortFlag(1);
    this->StartInteraction();
    this->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr);
    this->Interactor->Render();
}

void MyBoxWidget::OnRightButtonUp()
{
    if (this->State == MyBoxWidget::Outside)
    {
        return;
    }

    this->State = MyBoxWidget::Start;
    this->HighlightOutline(0);
    this->SizeHandles();

    this->EventCallbackCommand->SetAbortFlag(1);
    this->EndInteraction();
    this->InvokeEvent(vtkCommand::EndInteractionEvent, nullptr);
    this->Interactor->Render();
}

void MyBoxWidget::OnMouseMove()
{
    // See whether we're active
    if (this->State == MyBoxWidget::Outside || this->State == MyBoxWidget::Start)
    {
        return;
    }

    int X = this->Interactor->GetEventPosition()[0];
    int Y = this->Interactor->GetEventPosition()[1];

    // Do different things depending on state
    // Calculations everybody does
    double focalPoint[4], pickPoint[4], prevPickPoint[4];
    double z, vpn[3];

    vtkCamera* camera = this->CurrentRenderer->GetActiveCamera();
    if (!camera)
    {
        return;
    }

    // Compute the two points defining the motion vector
    this->ComputeWorldToDisplay(
        this->LastPickPosition[0], this->LastPickPosition[1], this->LastPickPosition[2], focalPoint);
    z = focalPoint[2];
    this->ComputeDisplayToWorld(double(this->Interactor->GetLastEventPosition()[0]),
                                double(this->Interactor->GetLastEventPosition()[1]), z, prevPickPoint);
    this->ComputeDisplayToWorld(double(X), double(Y), z, pickPoint);

    // Process the motion
    if (this->State == MyBoxWidget::Moving)
    {
        // Okay to process
        if (this->CurrentHandle)
        {
            if (this->RotationEnabled && this->CurrentHandle == this->HexFace)
            {
                camera->GetViewPlaneNormal(vpn);
                this->Rotate(X, Y, prevPickPoint, pickPoint, vpn);
            }
            else if (this->TranslationEnabled && this->CurrentHandle == this->Handle[6])
            {
                this->Translate(prevPickPoint, pickPoint);
            }
            else if (this->TranslationEnabled && this->ScalingEnabled)
            {
                if (this->CurrentHandle == this->Handle[0])
                {
                    this->MoveMinusXFace(prevPickPoint, pickPoint);
                }
                else if (this->CurrentHandle == this->Handle[1])
                {
                    this->MovePlusXFace(prevPickPoint, pickPoint);
                }
                else if (this->CurrentHandle == this->Handle[2])
                {
                    this->MoveMinusYFace(prevPickPoint, pickPoint);
                }
                else if (this->CurrentHandle == this->Handle[3])
                {
                    this->MovePlusYFace(prevPickPoint, pickPoint);
                }
                else if (this->CurrentHandle == this->Handle[4])
                {
                    this->MoveMinusZFace(prevPickPoint, pickPoint);
                }
                else if (this->CurrentHandle == this->Handle[5])
                {
                    this->MovePlusZFace(prevPickPoint, pickPoint);
                }
            }
        }
    }
    else if (this->ScalingEnabled && this->State == MyBoxWidget::Scaling)
    {
        this->Scale(prevPickPoint, pickPoint, X, Y);
    }

    // Interact, if desired
    this->EventCallbackCommand->SetAbortFlag(1);
    this->InvokeEvent(vtkCommand::InteractionEvent, nullptr);
    this->Interactor->Render();
}

void MyBoxWidget::MoveFace(
    double* p1, double* p2, double* dir, double* x1, double* x2, double* x3, double* x4, double* x5)
{
    int i;
    double v[3], v2[3];

    for (i = 0; i < 3; i++)
    {
        v[i] = p2[i] - p1[i];
        v2[i] = dir[i];
    }

    vtkMath::Normalize(v2);
    double f = vtkMath::Dot(v, v2);

    for (i = 0; i < 3; i++)
    {
        v[i] = f * v2[i];

        x1[i] += v[i];
        x2[i] += v[i];
        x3[i] += v[i];
        x4[i] += v[i];
        x5[i] += v[i];
    }
    this->PositionHandles();
}

void MyBoxWidget::GetDirection(
    const double Nx[3], const double Ny[3], const double Nz[3], double dir[3])
{
    double dotNy, dotNz;
    double y[3];

    if (vtkMath::Dot(Nx, Nx) != 0)
    {
        dir[0] = Nx[0];
        dir[1] = Nx[1];
        dir[2] = Nx[2];
    }
    else
    {
        dotNy = vtkMath::Dot(Ny, Ny);
        dotNz = vtkMath::Dot(Nz, Nz);
        if (dotNy != 0 && dotNz != 0)
        {
            vtkMath::Cross(Ny, Nz, dir);
        }
        else if (dotNy != 0)
        {
            // dir must have been initialized to the
            // corresponding coordinate direction before calling
            // this method
            vtkMath::Cross(Ny, dir, y);
            vtkMath::Cross(y, Ny, dir);
        }
        else if (dotNz != 0)
        {
            // dir must have been initialized to the
            // corresponding coordinate direction before calling
            // this method
            vtkMath::Cross(Nz, dir, y);
            vtkMath::Cross(y, Nz, dir);
        }
    }
}
void MyBoxWidget::MovePlusXFace(double* p1, double* p2)
{
    double* pts = static_cast<vtkDoubleArray*>(this->Points->GetData())->GetPointer(0);

    double* h1 = pts + 3 * 9;

    double* x1 = pts + 3 * 1;
    double* x2 = pts + 3 * 2;
    double* x3 = pts + 3 * 5;
    double* x4 = pts + 3 * 6;

    double dir[3] = { 1, 0, 0 };
    this->ComputeNormals();
    this->GetDirection(this->N[1], this->N[3], this->N[5], dir);
    this->MoveFace(p1, p2, dir, x1, x2, x3, x4, h1);
}

void MyBoxWidget::MoveMinusXFace(double* p1, double* p2)
{
    double* pts = static_cast<vtkDoubleArray*>(this->Points->GetData())->GetPointer(0);

    double* h1 = pts + 3 * 8;

    double* x1 = pts + 3 * 0;
    double* x2 = pts + 3 * 3;
    double* x3 = pts + 3 * 4;
    double* x4 = pts + 3 * 7;

    double dir[3] = { -1, 0, 0 };
    this->ComputeNormals();
    this->GetDirection(this->N[0], this->N[4], this->N[2], dir);

    this->MoveFace(p1, p2, dir, x1, x2, x3, x4, h1);
}

void MyBoxWidget::MovePlusYFace(double* p1, double* p2)
{
    double* pts = static_cast<vtkDoubleArray*>(this->Points->GetData())->GetPointer(0);

    double* h1 = pts + 3 * 11;

    double* x1 = pts + 3 * 2;
    double* x2 = pts + 3 * 3;
    double* x3 = pts + 3 * 6;
    double* x4 = pts + 3 * 7;

    double dir[3] = { 0, 1, 0 };
    this->ComputeNormals();
    this->GetDirection(this->N[3], this->N[5], this->N[1], dir);

    this->MoveFace(p1, p2, dir, x1, x2, x3, x4, h1);
}

void MyBoxWidget::MoveMinusYFace(double* p1, double* p2)
{
    double* pts = static_cast<vtkDoubleArray*>(this->Points->GetData())->GetPointer(0);

    double* h1 = pts + 3 * 10;

    double* x1 = pts + 3 * 0;
    double* x2 = pts + 3 * 1;
    double* x3 = pts + 3 * 4;
    double* x4 = pts + 3 * 5;

    double dir[3] = { 0, -1, 0 };
    this->ComputeNormals();
    this->GetDirection(this->N[2], this->N[0], this->N[4], dir);

    this->MoveFace(p1, p2, dir, x1, x2, x3, x4, h1);
}

void MyBoxWidget::MovePlusZFace(double* p1, double* p2)
{
    double* pts = static_cast<vtkDoubleArray*>(this->Points->GetData())->GetPointer(0);

    double* h1 = pts + 3 * 13;

    double* x1 = pts + 3 * 4;
    double* x2 = pts + 3 * 5;
    double* x3 = pts + 3 * 6;
    double* x4 = pts + 3 * 7;

    double dir[3] = { 0, 0, 1 };
    this->ComputeNormals();
    this->GetDirection(this->N[5], this->N[1], this->N[3], dir);

    this->MoveFace(p1, p2, dir, x1, x2, x3, x4, h1);
}

void MyBoxWidget::MoveMinusZFace(double* p1, double* p2)
{
    double* pts = static_cast<vtkDoubleArray*>(this->Points->GetData())->GetPointer(0);

    double* h1 = pts + 3 * 12;

    double* x1 = pts + 3 * 0;
    double* x2 = pts + 3 * 1;
    double* x3 = pts + 3 * 2;
    double* x4 = pts + 3 * 3;

    double dir[3] = { 0, 0, -1 };
    this->ComputeNormals();
    this->GetDirection(this->N[4], this->N[2], this->N[0], dir);

    this->MoveFace(p1, p2, dir, x1, x2, x3, x4, h1);
}

// Loop through all points and translate them
void MyBoxWidget::Translate(double* p1, double* p2)
{
    double* pts = static_cast<vtkDoubleArray*>(this->Points->GetData())->GetPointer(0);
    double v[3];

    v[0] = p2[0] - p1[0];
    v[1] = p2[1] - p1[1];
    v[2] = p2[2] - p1[2];

    // Move the corners
    for (int i = 0; i < 8; i++)
    {
        *pts++ += v[0];
        *pts++ += v[1];
        *pts++ += v[2];
    }
    this->PositionHandles();
}

void MyBoxWidget::Scale(double* vtkNotUsed(p1), double* vtkNotUsed(p2), int vtkNotUsed(X), int Y)
{
    double* pts = static_cast<vtkDoubleArray*>(this->Points->GetData())->GetPointer(0);
    double* center = static_cast<vtkDoubleArray*>(this->Points->GetData())->GetPointer(3 * 14);
    double sf;

    if (Y > this->Interactor->GetLastEventPosition()[1])
    {
        sf = 1.03;
    }
    else
    {
        sf = 0.97;
    }

    // Move the corners
    for (int i = 0; i < 8; i++, pts += 3)
    {
        pts[0] = sf * (pts[0] - center[0]) + center[0];
        pts[1] = sf * (pts[1] - center[1]) + center[1];
        pts[2] = sf * (pts[2] - center[2]) + center[2];
    }
    this->PositionHandles();
}

void MyBoxWidget::ComputeNormals()
{
    double* pts = static_cast<vtkDoubleArray*>(this->Points->GetData())->GetPointer(0);
    double* p0 = pts;
    double* px = pts + 3 * 1;
    double* py = pts + 3 * 3;
    double* pz = pts + 3 * 4;
    int i;

    for (i = 0; i < 3; i++)
    {
        this->N[0][i] = p0[i] - px[i];
        this->N[2][i] = p0[i] - py[i];
        this->N[4][i] = p0[i] - pz[i];
    }
    vtkMath::Normalize(this->N[0]);
    vtkMath::Normalize(this->N[2]);
    vtkMath::Normalize(this->N[4]);
    for (i = 0; i < 3; i++)
    {
        this->N[1][i] = -this->N[0][i];
        this->N[3][i] = -this->N[2][i];
        this->N[5][i] = -this->N[4][i];
    }
}

void MyBoxWidget::GetPlanes(vtkPlanes* planes)
{
    if (!planes)
    {
        return;
    }

    this->ComputeNormals();

    vtkPoints* pts = vtkPoints::New(VTK_DOUBLE);
    pts->SetNumberOfPoints(6);

    vtkDoubleArray* normals = vtkDoubleArray::New();
    normals->SetNumberOfComponents(3);
    normals->SetNumberOfTuples(6);

    // Set the normals and coordinate values
    double factor = (this->InsideOut ? -1.0 : 1.0);
    for (int i = 0; i < 6; i++)
    {
        pts->SetPoint(i, this->Points->GetPoint(8 + i));
        normals->SetTuple3(i, factor * this->N[i][0], factor * this->N[i][1], factor * this->N[i][2]);
    }

    planes->SetPoints(pts);
    planes->SetNormals(normals);

    pts->Delete();
    normals->Delete();
}

void MyBoxWidget::Rotate(int X, int Y, double* p1, double* p2, double* vpn)
{
    double* pts = static_cast<vtkDoubleArray*>(this->Points->GetData())->GetPointer(0);
    double* center = static_cast<vtkDoubleArray*>(this->Points->GetData())->GetPointer(3 * 14);
    double v[3];    // vector of motion
    double axis[3]; // axis of rotation
    double theta;   // rotation angle
    int i;

    v[0] = p2[0] - p1[0];
    v[1] = p2[1] - p1[1];
    v[2] = p2[2] - p1[2];

    // Create axis of rotation and angle of rotation
    vtkMath::Cross(vpn, v, axis);
    if (vtkMath::Normalize(axis) == 0.0)
    {
        return;
    }
    const int* size = this->CurrentRenderer->GetSize();
    double l2 = (X - this->Interactor->GetLastEventPosition()[0]) *
                    (X - this->Interactor->GetLastEventPosition()[0]) +
                (Y - this->Interactor->GetLastEventPosition()[1]) *
                    (Y - this->Interactor->GetLastEventPosition()[1]);
    theta = 360.0 * sqrt(l2 / (size[0] * size[0] + size[1] * size[1]));

    // Manipulate the transform to reflect the rotation
    this->Transform->Identity();
    this->Transform->Translate(center[0], center[1], center[2]);
    this->Transform->RotateWXYZ(theta, axis);
    this->Transform->Translate(-center[0], -center[1], -center[2]);

    // Set the corners
    vtkPoints* newPts = vtkPoints::New(VTK_DOUBLE);
    this->Transform->TransformPoints(this->Points, newPts);

    for (i = 0; i < 8; i++, pts += 3)
    {
        this->Points->SetPoint(i, newPts->GetPoint(i));
    }

    newPts->Delete();
    this->PositionHandles();
}

void MyBoxWidget::CreateDefaultProperties()
{
    // Handle properties
    this->HandleProperty = vtkProperty::New();
    this->HandleProperty->SetColor(1, 1, 1);

    this->SelectedHandleProperty = vtkProperty::New();
    this->SelectedHandleProperty->SetColor(1, 0, 0);

    // Face properties
    this->FaceProperty = vtkProperty::New();
    this->FaceProperty->SetColor(1, 1, 1);
    this->FaceProperty->SetOpacity(0.0);

    this->SelectedFaceProperty = vtkProperty::New();
    this->SelectedFaceProperty->SetColor(1, 1, 0);
    this->SelectedFaceProperty->SetOpacity(0.25);

    // Outline properties
    this->OutlineProperty = vtkProperty::New();
    this->OutlineProperty->SetRepresentationToWireframe();
    this->OutlineProperty->SetAmbient(1.0);
    this->OutlineProperty->SetAmbientColor(1.0, 1.0, 1.0);
    this->OutlineProperty->SetLineWidth(2.0);

    this->SelectedOutlineProperty = vtkProperty::New();
    this->SelectedOutlineProperty->SetRepresentationToWireframe();
    this->SelectedOutlineProperty->SetAmbient(1.0);
    this->SelectedOutlineProperty->SetAmbientColor(0.0, 1.0, 0.0);
    this->SelectedOutlineProperty->SetLineWidth(2.0);
}

void MyBoxWidget::PlaceWidget(double bds[6])
{
    int i;
    double bounds[6], center[3];

    this->AdjustBounds(bds, bounds, center);

    this->Points->SetPoint(0, bounds[0], bounds[2], bounds[4]);
    this->Points->SetPoint(1, bounds[1], bounds[2], bounds[4]);
    this->Points->SetPoint(2, bounds[1], bounds[3], bounds[4]);
    this->Points->SetPoint(3, bounds[0], bounds[3], bounds[4]);
    this->Points->SetPoint(4, bounds[0], bounds[2], bounds[5]);
    this->Points->SetPoint(5, bounds[1], bounds[2], bounds[5]);
    this->Points->SetPoint(6, bounds[1], bounds[3], bounds[5]);
    this->Points->SetPoint(7, bounds[0], bounds[3], bounds[5]);

    for (i = 0; i < 6; i++)
    {
        this->InitialBounds[i] = bounds[i];
    }
    this->InitialLength = sqrt((bounds[1] - bounds[0]) * (bounds[1] - bounds[0]) +
                               (bounds[3] - bounds[2]) * (bounds[3] - bounds[2]) +
                               (bounds[5] - bounds[4]) * (bounds[5] - bounds[4]));

    this->PositionHandles();
    this->ComputeNormals();
    this->SizeHandles();
}

void MyBoxWidget::GetTransform(vtkTransform* t)
{
    double* pts = static_cast<vtkDoubleArray*>(this->Points->GetData())->GetPointer(0);
    double* p0 = pts;
    double* p1 = pts + 3 * 1;
    double* p3 = pts + 3 * 3;
    double* p4 = pts + 3 * 4;
    double* p14 = pts + 3 * 14;
    double center[3], translate[3], scale[3], scaleVec[3][3];
    double InitialCenter[3];
    int i;

    // The transformation is relative to the initial bounds.
    // Initial bounds are set when PlaceWidget() is invoked.
    t->Identity();

    // Translation
    for (i = 0; i < 3; i++)
    {
        InitialCenter[i] = (this->InitialBounds[2 * i + 1] + this->InitialBounds[2 * i]) / 2.0;
        center[i] = p14[i] - InitialCenter[i];
    }
    translate[0] = center[0] + InitialCenter[0];
    translate[1] = center[1] + InitialCenter[1];
    translate[2] = center[2] + InitialCenter[2];
    t->Translate(translate[0], translate[1], translate[2]);

    // Orientation
    vtkMatrix4x4* matrix = vtkMatrix4x4::New();
    this->PositionHandles();
    this->ComputeNormals();
    for (i = 0; i < 3; i++)
    {
        matrix->SetElement(i, 0, this->N[1][i]);
        matrix->SetElement(i, 1, this->N[3][i]);
        matrix->SetElement(i, 2, this->N[5][i]);
    }
    t->Concatenate(matrix);
    matrix->Delete();

    // Scale
    for (i = 0; i < 3; i++)
    {
        scaleVec[0][i] = (p1[i] - p0[i]);
        scaleVec[1][i] = (p3[i] - p0[i]);
        scaleVec[2][i] = (p4[i] - p0[i]);
    }

    scale[0] = vtkMath::Norm(scaleVec[0]);
    if (this->InitialBounds[1] != this->InitialBounds[0])
    {
        scale[0] = scale[0] / (this->InitialBounds[1] - this->InitialBounds[0]);
    }
    scale[1] = vtkMath::Norm(scaleVec[1]);
    if (this->InitialBounds[3] != this->InitialBounds[2])
    {
        scale[1] = scale[1] / (this->InitialBounds[3] - this->InitialBounds[2]);
    }
    scale[2] = vtkMath::Norm(scaleVec[2]);
    if (this->InitialBounds[5] != this->InitialBounds[4])
    {
        scale[2] = scale[2] / (this->InitialBounds[5] - this->InitialBounds[4]);
    }
    t->Scale(scale[0], scale[1], scale[2]);

    // Add back in the contribution due to non-origin center
    t->Translate(-InitialCenter[0], -InitialCenter[1], -InitialCenter[2]);
}

void MyBoxWidget::SetTransform(vtkTransform* t)
{
    if (!t)
    {
        vtkErrorMacro(<< "vtkTransform t must be non-nullptr");
        return;
    }

    double* pts = static_cast<vtkDoubleArray*>(this->Points->GetData())->GetPointer(0);
    double xIn[3];
    // make sure the transform is up-to-date before using it
    t->Update();

    // Position the eight points of the box and then update the
    // position of the other handles.
    double* bounds = this->InitialBounds;

    xIn[0] = bounds[0];
    xIn[1] = bounds[2];
    xIn[2] = bounds[4];
    t->InternalTransformPoint(xIn, pts);

    xIn[0] = bounds[1];
    xIn[1] = bounds[2];
    xIn[2] = bounds[4];
    t->InternalTransformPoint(xIn, pts + 3);

    xIn[0] = bounds[1];
    xIn[1] = bounds[3];
    xIn[2] = bounds[4];
    t->InternalTransformPoint(xIn, pts + 6);

    xIn[0] = bounds[0];
    xIn[1] = bounds[3];
    xIn[2] = bounds[4];
    t->InternalTransformPoint(xIn, pts + 9);

    xIn[0] = bounds[0];
    xIn[1] = bounds[2];
    xIn[2] = bounds[5];
    t->InternalTransformPoint(xIn, pts + 12);

    xIn[0] = bounds[1];
    xIn[1] = bounds[2];
    xIn[2] = bounds[5];
    t->InternalTransformPoint(xIn, pts + 15);

    xIn[0] = bounds[1];
    xIn[1] = bounds[3];
    xIn[2] = bounds[5];
    t->InternalTransformPoint(xIn, pts + 18);

    xIn[0] = bounds[0];
    xIn[1] = bounds[3];
    xIn[2] = bounds[5];
    t->InternalTransformPoint(xIn, pts + 21);

    this->PositionHandles();
}

void MyBoxWidget::GetPolyData(vtkPolyData* pd)
{
    pd->SetPoints(this->HexPolyData->GetPoints());
    pd->SetPolys(this->HexPolyData->GetPolys());
}

void MyBoxWidget::SetOutlineFaceWires(int newValue)
{
    if (this->OutlineFaceWires != newValue)
    {
        this->OutlineFaceWires = newValue;
        this->Modified();
        // the outline is dependent on this value, so we have to regen
        this->GenerateOutline();
    }
}

void MyBoxWidget::SetOutlineCursorWires(int newValue)
{
    if (this->OutlineCursorWires != newValue)
    {
        this->OutlineCursorWires = newValue;
        this->Modified();
        // the outline is dependent on this value, so we have to regen
        this->GenerateOutline();
    }
}

void MyBoxWidget::GenerateOutline()
{
    // Whatever the case may be, we have to reset the Lines of the
    // OutlinePolyData (i.e. nuke all current line data)
    vtkCellArray* cells = this->OutlinePolyData->GetLines();
    cells->Reset();

    // Now the outline lines
    if (!this->OutlineFaceWires && !this->OutlineCursorWires)
    {
        return;
    }

    vtkIdType pts[2];

    if (this->OutlineFaceWires)
    {
        pts[0] = 0;
        pts[1] = 7; // the -x face
        cells->InsertNextCell(2, pts);
        pts[0] = 3;
        pts[1] = 4;
        cells->InsertNextCell(2, pts);
        pts[0] = 1;
        pts[1] = 6; // the +x face
        cells->InsertNextCell(2, pts);
        pts[0] = 2;
        pts[1] = 5;
        cells->InsertNextCell(2, pts);
        pts[0] = 1;
        pts[1] = 4; // the -y face
        cells->InsertNextCell(2, pts);
        pts[0] = 0;
        pts[1] = 5;
        cells->InsertNextCell(2, pts);
        pts[0] = 3;
        pts[1] = 6; // the +y face
        cells->InsertNextCell(2, pts);
        pts[0] = 2;
        pts[1] = 7;
        cells->InsertNextCell(2, pts);
        pts[0] = 0;
        pts[1] = 2; // the -z face
        cells->InsertNextCell(2, pts);
        pts[0] = 1;
        pts[1] = 3;
        cells->InsertNextCell(2, pts);
        pts[0] = 4;
        pts[1] = 6; // the +Z face
        cells->InsertNextCell(2, pts);
        pts[0] = 5;
        pts[1] = 7;
        cells->InsertNextCell(2, pts);
    }
    if (this->OutlineCursorWires)
    {
        pts[0] = 8;
        pts[1] = 9; // the x cursor line
        cells->InsertNextCell(2, pts);
        pts[0] = 10;
        pts[1] = 11; // the y cursor line
        cells->InsertNextCell(2, pts);
        pts[0] = 12;
        pts[1] = 13; // the z cursor line
        cells->InsertNextCell(2, pts);
    }
    this->OutlinePolyData->Modified();
    if (this->OutlineProperty)
    {
        this->OutlineProperty->SetRepresentationToWireframe();
        this->SelectedOutlineProperty->SetRepresentationToWireframe();
    }
}

//------------------------------------------------------------------------------
void MyBoxWidget::RegisterPickers()
{
    vtkPickingManager* pm = this->GetPickingManager();
    if (!pm)
    {
        return;
    }
    pm->AddPicker(this->HandlePicker, this);
    pm->AddPicker(this->HexPicker, this);
}
VTK_ABI_NAMESPACE_END
